using System;
using System.Threading;
using System.Text;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using GHIElectronics.NETMF.FEZ;
using GHIElectronics.NETMF.Net;
using GHIElectronics.NETMF.Hardware;
using GHIElectronics.NETMF.Net.Sockets;
using GHIElectronics.NETMF.Net.NetworkInformation;
using Socket = GHIElectronics.NETMF.Net.Sockets.Socket;

namespace FEZ_Panda_II_ApplicationTCPServer
{

    public class Program
    {
        // 旗の動きを調節するモーター
        static OutputPort flagLeft = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di50, false);
        static OutputPort flagRight = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di52, false);

        // アナログで光量を調節できる上端の LED
        static PWM topLEDLeft = new PWM((PWM.Pin)FEZ_Pin.PWM.Di8);
        static PWM topLEDRight = new PWM((PWM.Pin)FEZ_Pin.PWM.Di9);

        // メニューを表示する LED
        static OutputPort menuLED1 = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di21, false);
        static OutputPort menuLED2 = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di23, false);
        static OutputPort menuLED3 = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di25, false);
        static OutputPort menuLED4 = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di27, false);
        static OutputPort menuLED5 = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di29, false);
        static OutputPort menuLED6 = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di31, false);

        // 現在のモード 0 : 待機状態 / 1 : ユーザの認識 / 2 : ユーザが
        static int viewMode = 0;

        // アナログで調光する LED のゆらぎを演出するためのカウンター
        static int actionCnt = 0;


        /**
         * Main 処理
         */
        public static void Main()
        {
            // サーバプロセスの起動
            Thread workerThread = new Thread(Program.analogLEDWork);
            workerThread.Start();

            const Int32 c_port = 80;
			
			// PC のネットワーク情報を設定
            byte[] ip = { 000, 000, 00, 000 };
            byte[] subnet = { 255, 255, 0, 0 };
            byte[] gateway = { 000, 000, 00, 0 };
            byte[] mac = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

            WIZnet_W5100.Enable(SPI.SPI_module.SPI1, (Cpu.Pin)FEZ_Pin.Digital.Di10, (Cpu.Pin)FEZ_Pin.Digital.Di5, true);
            NetworkInterface.EnableStaticIP(ip, subnet, gateway, mac);
            NetworkInterface.EnableStaticDns(new byte[] { 000, 000, 00, 0 });
            HttpListener listener = new HttpListener("http", c_port);
            listener.Start();

            while (true)
            {
                HttpListenerResponse response = null;
                HttpListenerContext context = null;


                try
                {
                    context = listener.GetContext();

                    if (context == null)
                    {
                        continue;
                    }

                    // 疎通確認用のレスポンス
                    response = context.Response;
                    response.StatusCode = (int)HttpStatusCode.OK;
                    Debug.Print("get request2");
                    byte[] HTML = Encoding.UTF8.GetBytes(
                        "<html><body>" +
                        "<h1>FEZ Panda</h1>" +
                        "<p>Embedded Systems were never easier!</p>" +
                        "</body></html>");
                    response.ContentType = "text/html";
                    response.OutputStream.Write(HTML, 0, HTML.Length);
                    response.Close();


                    // 送られてきた URL によって処理を分岐
                    // wait : 停止処理
                    // detect : ユーザ検知
                    // menuReset : メニューのリセット
                    // menu0 - 5 : それぞれのメニューをユーザがタッチ
                    switch (context.Request.RawUrl)
                    {
                        case "/wait":
                            viewMode = 0;
                            actionCnt = 0;
                            menuLED1.Write(false);
                            menuLED2.Write(false);
                            menuLED3.Write(false);
                            menuLED4.Write(false);
                            menuLED5.Write(false);
                            menuLED6.Write(false);
                            flagLeft.Write(true);
                            flagRight.Write(true);
                            break;
                        case "/detect":
                            viewMode = 1;
                            actionCnt = 0;
                            menuLED1.Write(false);
                            menuLED2.Write(false);
                            menuLED3.Write(false);
                            menuLED4.Write(false);
                            menuLED5.Write(false);
                            menuLED6.Write(false);
                            flagLeft.Write(false);
                            flagRight.Write(false);
                            break;
                        default:
                            viewMode = 2;
                            if (viewMode != 2)
                            {
                                viewMode = 2;
                            }

                            switch (context.Request.RawUrl)
                            {
                                case "/menuReset":
                                    menuLED1.Write(false);
                                    menuLED2.Write(false);
                                    menuLED3.Write(false);
                                    menuLED4.Write(false);
                                    menuLED5.Write(false);
                                    menuLED6.Write(false);
                                    break;
                                case "/menu0":
                                    actionCnt = 0;
                                    menuLED1.Write(true);
                                    menuLED2.Write(false);
                                    menuLED3.Write(false);
                                    menuLED4.Write(false);
                                    menuLED5.Write(false);
                                    menuLED6.Write(false);
                                    break;
                                case "/menu1":
                                    actionCnt = 0;
                                    menuLED1.Write(false);
                                    menuLED2.Write(true);
                                    menuLED3.Write(false);
                                    menuLED4.Write(false);
                                    menuLED5.Write(false);
                                    menuLED6.Write(false);
                                    break;
                                case "/menu2":
                                    actionCnt = 0;
                                    menuLED1.Write(false);
                                    menuLED2.Write(false);
                                    menuLED3.Write(true);
                                    menuLED4.Write(false);
                                    menuLED5.Write(false);
                                    menuLED6.Write(false);
                                    break;
                                case "/menu3":
                                    actionCnt = 0;
                                    menuLED1.Write(false);
                                    menuLED2.Write(false);
                                    menuLED3.Write(false);
                                    menuLED4.Write(true);
                                    menuLED5.Write(false);
                                    menuLED6.Write(false);
                                    break;
                                case "/menu4":
                                    actionCnt = 0;
                                    menuLED1.Write(false);
                                    menuLED2.Write(false);
                                    menuLED3.Write(false);
                                    menuLED4.Write(false);
                                    menuLED5.Write(true);
                                    menuLED6.Write(false);
                                    break;
                                case "/menu5":
                                    actionCnt = 0;
                                    menuLED1.Write(false);
                                    menuLED2.Write(false);
                                    menuLED3.Write(false);
                                    menuLED4.Write(false);
                                    menuLED5.Write(false);
                                    menuLED6.Write(true);
                                    break;
                            }
                            break;
                    }
                }
                catch
                {
                    Debug.Print("caught exception");
                    if (context != null)
                    {
                        context.Close();
                    }
                }
            }
        }


        /**
         * アナログの LED の調光
         */
        public static void analogLEDWork()
        {
            while (true)
            {

                switch (viewMode)
                {
                    case 0:
                        waitAction();
                        break;
                    case 1:
                        detectAction();
                        break;
                    case 2:
                        touchAction();
                        break;
                }

                Thread.Sleep(50);
            }
        }
        
        private static void waitAction()
        {
            if (actionCnt < 10)
            {
                topLEDLeft.Set(10000, (byte) (actionCnt * 3));
                topLEDRight.Set(10000, (byte) (actionCnt * 3));
            }
            else if (actionCnt < 20)
            {
                topLEDLeft.Set(10000, (byte)(60 - actionCnt * 3));
                topLEDRight.Set(10000, (byte)(60 - actionCnt * 3));
            }
            else
            {
                topLEDLeft.Set(10000, 0);
                topLEDRight.Set(10000, 0);
            }

            actionCnt++;
            if (actionCnt > 40) {
                actionCnt = 0;
            }
        }

        /**
         * ユーザの認識
         */
        private static void detectAction()
        {
            if (actionCnt < 7)
            {
                if (actionCnt < 4)
                {
                    topLEDLeft.Set(10000, (byte)(actionCnt * 8));
                }
                else
                {
                    topLEDLeft.Set(10000, (byte)((7 - actionCnt) * 8));
                }
                topLEDRight.Set(10000, 0);
            }
            else
            {
                topLEDLeft.Set(10000, 0);
                if (actionCnt < 10)
                {
                    topLEDRight.Set(10000, (byte)((actionCnt - 7) * 8));
                }
                else
                {
                    topLEDRight.Set(10000, (byte)((13 - actionCnt) * 8));
                }
            }
            
            // メニューの LED を激しく点滅
            switch((byte) (actionCnt)) {
                case 0:
                case 12:
                    menuLED1.Write(false);
                    menuLED2.Write(false);
                    menuLED3.Write(false);
                    menuLED4.Write(false);
                    menuLED5.Write(false);
                    menuLED6.Write(false);
                    break;
                case 1:
                case 11:
                    menuLED1.Write(true);
                    menuLED2.Write(false);
                    menuLED3.Write(false);
                    menuLED4.Write(false);
                    menuLED5.Write(false);
                    menuLED6.Write(false);
                    break;
                case 2:
                case 10:
                    menuLED1.Write(false);
                    menuLED2.Write(true);
                    menuLED3.Write(false);
                    menuLED4.Write(false);
                    menuLED5.Write(false);
                    menuLED6.Write(false);
                    break;
                case 3:
                case 9:
                    menuLED1.Write(false);
                    menuLED2.Write(false);
                    menuLED3.Write(true);
                    menuLED4.Write(false);
                    menuLED5.Write(false);
                    menuLED6.Write(false);
                    break;
                case 4:
                case 8:
                    menuLED1.Write(false);
                    menuLED2.Write(false);
                    menuLED3.Write(false);
                    menuLED4.Write(true);
                    menuLED5.Write(false);
                    menuLED6.Write(false);
                    break;
                case 5:
                case 7:
                    menuLED1.Write(false);
                    menuLED2.Write(false);
                    menuLED3.Write(false);
                    menuLED4.Write(false);
                    menuLED5.Write(true);
                    menuLED6.Write(false);
                    break;
                case 6:
                    menuLED1.Write(false);
                    menuLED2.Write(false);
                    menuLED3.Write(false);
                    menuLED4.Write(false);
                    menuLED5.Write(false);
                    menuLED6.Write(true);
                    break;
            }

            actionCnt++;
            if (actionCnt > 13)
            {
                actionCnt = 0;
            }
        }

        /**
         * ユーザが近づいたときの処理
         */
        private static void touchAction()
        {
            if (actionCnt < 11)
            {
                topLEDLeft.Set(10000, (byte)((10 - actionCnt) * 3));
                topLEDRight.Set(10000, (byte)((10 - actionCnt) * 3));
            }
            actionCnt++;
            if (actionCnt > 10000)
            {
                actionCnt = 11;
            }
        }
    }
}
